import torch
import torch.nn.functional as F
import torch.nn as nn
# input = torch.randn(2, 3)       #算出来的最后一层的x的值 
#使用固定值
input = torch.tensor([
                      [-1.2,  -0.6123, -0.5582], # 样本1的3个类别得分
                      [0.6806,-1.2833, 2.5749],  # 样本2的3个类别得分
                      [1.5,    0.7452, -0.541]  # 样本3的3个类别得分
                    ])
target = torch.tensor([0, 1, 2])   #y_ic

# 使用公式来计算
one_hot = F.one_hot(target).float() # 对标签进行one_hot编码  y_ic
exp = torch.exp(input)
sum_exp = torch.sum(torch.exp(input), dim = 1) # 沿 dim=1 求和（对每行的3个元素求和） 结果就是一行3列
sum_exp_reshape = sum_exp.reshape(-1,1)  # 一维转成二维  1行3列 转成 3行1列
p = exp / sum_exp_reshape # [3,3] / [3,1] python个个广播机制 自动将 [3,1]变成[3,3]方法是将每行的单个值复制3次,现在两个都是 [3, 3] 形状，进行逐元素相除
print("********************\n")
print(f"exp:\n{exp} \n")
print(f"sum_exp:\n{sum_exp} \n")
print(f"sum_exp_reshape:\n{sum_exp_reshape} \n")
print(f"p:\n{p} \n")
print("********************\n")
# p = torch.exp(input)/torch.sum(torch.exp(input), dim = 1).reshape(-1, 1) #通过softmax函数求出p_ic
loss_com = -torch.sum(one_hot * torch.log(p)) / target.shape[0] #当两个形状相同的张量使用 * 运算符时，PyTorch 会进行逐元素乘法（element-wise multiplication），也叫 Hadamard 乘积。


# 下面用 torch.nn.function 实现
log_softmax = F.log_softmax(input, dim = 1)
loss_func = F.nll_loss(log_softmax, target) # 无需对标签做 one_hot 编码


# 最后我们直接用 torch.nn.CrossEntropyLoss 验证
loss_entropy = F.cross_entropy(input, target)

print(f"input:\n{input} \n")
print(f"target:\n{target} \n")    
print(f"one_hot:\n{one_hot} \n")
print(f"p(softmax(input)):\n{p} \n")   
print(f"log_softmax:\n{log_softmax}\n")
print(f"loss_com:{loss_com}; \nloss_func:{loss_func};\n loss_entropy:{loss_entropy}")